package de.g18.it0a.jlaser.gui.view;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;

import de.g18.it0a.jlaser.domain.Laser;
import de.g18.it0a.jlaser.domain.Mirror;
import de.g18.it0a.jlaser.domain.Position;
import de.g18.it0a.jlaser.domain.Room;
import de.g18.it0a.jlaser.domain.RoomConfiguration;


public class SolutionView {

    private final Room room;
    private final BufferedImage renderedRoom;

    private BufferedImage image;
    private Graphics2D graphics;


    public SolutionView(Room aRoom) {
        room = aRoom;
        renderedRoom = new RoomView(room).render();
    }

    public BufferedImage render() {
        if (room == null) {
            return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
        }

        Graphics2D g = renderedRoom.createGraphics();
        g.setFont(g.getFont().deriveFont(Font.BOLD));
        FontMetrics metrics = g.getFontMetrics();
        g.dispose();

        image = new BufferedImage(renderedRoom.getWidth() + (metrics.stringWidth("000,000") + 20) * 2,
                                  renderedRoom.getHeight() + (metrics.getHeight() + 20) * 2,
                                  BufferedImage.TYPE_INT_ARGB);
        graphics = image.createGraphics();

        Position centerPosition = getCenterPosition(renderedRoom);
        graphics.drawImage(renderedRoom, centerPosition.getX(), centerPosition.getY(), null);
        paintLaser();

        graphics.dispose();
        return image;
    }

    private void paintLaser() {
        Laser laser = room.getLaser();
        Position lastPosition = laser.getPosition();
        Mirror lastMirror = null;
        for (Mirror mirror : room.getSortedUsedMirrors()) {
            drawPosition(lastPosition);
            graphics.setColor(Color.RED);
            graphics.draw(new Line2D.Double(transformToPaintPosition(lastPosition).getPoint(),
                                            transformToPaintPosition(mirror.getPosition()).getPoint()));

            lastMirror = mirror;
            lastPosition = lastMirror.getPosition();
        }

        if (lastMirror != null) {
            drawPosition(lastPosition);
            drawPosition(lastMirror.getPointedPosition());
            graphics.setColor(Color.RED);
            graphics.draw(new Line2D.Double(transformToPaintPosition(lastMirror.getPosition()).getPoint(),
                                            transformToPaintPosition(lastMirror.getPointedPosition()).getPoint()));
        }
    }


    private void drawPosition(Position aPosition) {
        Position relativMirrorPosition = transformToPaintPosition(aPosition);

        graphics.setColor(Color.GRAY);
        graphics.setFont(graphics.getFont().deriveFont(Font.BOLD));
        FontMetrics metrics = graphics.getFontMetrics();
        String text = aPosition.getX() + "," + aPosition.getY();
        int textWidth = metrics.stringWidth(text);
        switch (WallPosition.resolve(room.getConfig(), aPosition)) {
            case NORTH:
                graphics.drawRect(relativMirrorPosition.getX(), relativMirrorPosition.getY() - 10, 0, 10);
                graphics.drawString(text, relativMirrorPosition.getX() - textWidth / 2, relativMirrorPosition.getY() - 15);
                break;

            case EAST:
                graphics.drawRect(relativMirrorPosition.getX(), relativMirrorPosition.getY(), 10, 0);
                graphics.drawString(text, relativMirrorPosition.getX() + 15, relativMirrorPosition.getY() + metrics.getHeight() / 2);
                break;

            case SOUTH:
                graphics.drawRect(relativMirrorPosition.getX(), relativMirrorPosition.getY(), 0, 10);
                graphics.drawString(text, relativMirrorPosition.getX() - textWidth / 2, relativMirrorPosition.getY() + metrics.getHeight() + 15);
                break;

            case WEST:
                graphics.drawRect(relativMirrorPosition.getX() - 10, relativMirrorPosition.getY(), 10, 0);
                graphics.drawString(text, relativMirrorPosition.getX() - textWidth - 15, relativMirrorPosition.getY() + metrics.getHeight() / 2);
                break;
        }
    }

    private Position transformToPaintPosition(Position aPosition) {
        return new Position(aPosition.getX() + (image.getWidth() - renderedRoom.getWidth()) / 2,
                            room.getConfig().getHeight() - aPosition.getY() + (image.getHeight() - renderedRoom.getHeight()) / 2);
    }

    private Position getCenterPosition(BufferedImage aImage) {
        return new Position((image.getWidth() - aImage.getWidth()) / 2,
                            (image.getHeight() - aImage.getHeight()) / 2);
    }

    // Inner classes ***********************************************************

    private static enum WallPosition {
        NORTH,
        EAST,
        SOUTH,
        WEST;

        public static WallPosition resolve(RoomConfiguration aConfig, Position aPosition) {
            if (aPosition.getX() == 0) {
                return WEST;
            } else if (aPosition.getX() == aConfig.getWidth()) {
                return EAST;
            } else if (aPosition.getY() == 0) {
                return SOUTH;
            } else if (aPosition.getY() == aConfig.getHeight()) {
                return NORTH;
            }
            throw new IllegalArgumentException(aPosition.toString() + " does not represent a point on the wall!");
        }
    }
}
